// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using CIP4.Common;
using CIP4.Item;
using CIP4.Simple;

namespace CIP4.Query
{
    public class CIP4QueryType<CData> : IOrderedQueryable<CData>, ICIP4QueryBase
    {
        public StrongWrapper<CData> Wrapper { get; set; }

        public string CollectionExternalID { get; set; }

        public CIP4QueryType()
        {
            Wrapper = new StrongWrapper<CData>(Globals.DefaultLogger);
            Type t = typeof(CData);
            CIP4ItemAttribute itemAttribute = CIP4ItemAttribute.GetAttribute(t);
            if (itemAttribute == null)
                throw new ArgumentException(string.Format("The type {0} cannot be used with a CIP4QueryType because it does not have a CIP4ItemAttribute associated with it", t.Name));
            CollectionExternalID = itemAttribute.Collection;
            if (string.IsNullOrEmpty(CollectionExternalID))
                throw new ArgumentException(string.Format("The type {0} cannot be used with a CIP4QueryType because its CIP4ItemAttribute does not have a collection", t.Name));

            Provider = new CIP4QueryProvider<CData>(CollectionExternalID);
            Expression = Expression.Constant(this);
        }

        public CIP4QueryType(IQueryProvider provider, Expression expression)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            if (!typeof(IQueryable<CData>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentOutOfRangeException("expression");
            }

            Provider = provider;
            Expression = expression;
        }

        #region IEnumerable<CData> Members

        public IEnumerator<CData> GetEnumerator()
        {
            // attach the colection here if necessary?
            IEnumerable<CData> firstResult = Provider.Execute<IEnumerable<CData>>(Expression);
            IEnumerator<CData> result = firstResult.GetEnumerator();
            return result;
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (Provider.Execute<System.Collections.IEnumerable>(Expression)).GetEnumerator();
        }

        #endregion

        #region IQueryable Members

        public Type ElementType
        {
            get { return typeof(CData); }
        }

        public Expression Expression
        {
            get; 
            private set;
        }

        public IQueryProvider Provider
        {
            get;
            private set;
        }

        #endregion
    }

    interface ICIP4QueryBase
    {
        string CollectionExternalID { get; }
    }
}
